#ifndef GsgMeta_defined
#define GsgMeta_defined

#include <string>
#include <vector>

/// Generic metadata interface class
///
/// provides base class for access to the contents of
/// user defined classes.
///
/// can be used for:
///
/// * serialisation
/// * garbage collection
/// * verification
/// * editing
///
class GsgMeta
{
public:
  virtual void attr(const char *name, float &value) = 0;
  virtual void attr(const char *name, int &value) = 0;
  virtual void attr(const char *name, float *value, int n) = 0;
  virtual void attr(const char *name, int *value, int n) = 0;
  virtual void attr(const char *name, std::string &value) = 0;
  virtual void push() = 0;
  virtual void pop() = 0;
  virtual bool begin_element(const char *type_name) = 0;
  virtual void end_element() = 0;
  virtual bool is_reading() = 0;
  virtual const char *cur_element_name() = 0;

  // generalised single element
  template<class Class> void element(Class &value)
  {
    push();
    if ( begin_element( Class::type_name() ) )
    {
      value.meta(*this);
      end_element();
    }
    pop();
  }

  // generalised multiple elements
  template<class Class> void elements(std::vector<Class> &value)
  {
    push();
    if (is_reading() )
    {
      value.resize(0);
      for (size_t i = 0; begin_element( Class::typeName() ); ++i)
      {
        value.push_back( Class() );
        value.back().meta(*this);
      }
    } else
    {
      for (size_t i = 0; i != value.size() && begin_element( Class::typeName() ); ++i)
      {
        value[i].meta(*this);
      }
    }
    
    pop();
  }

  // generalised multiple polymorphic elements
  template<class Class> void elements(std::vector<Class*> &value)
  {
    push();
    if (is_reading() )
    {
      const char *type_name = NULL;
      value.resize(0);
      for (size_t i = 0; ; ++i)
      {
        Class *poly_ptr = Class::Create( cur_element_name() );
        begin_element( cur_element_name() );
        value.push_back(poly_ptr);
        poly_ptr->meta(*this);
        end_element();
      }
    } else
    {
      for (size_t i = 0; i != value.size() && begin_element( value[i]->dynamic_type_name() ); ++i)
      {
        value[i]->meta(*this);
        end_element();
      }
    }
    
    pop();
  }
};

#endif
